import ast
import json
import logging
import os
import re
import shutil
import sys
import zipfile
from dataclasses import dataclass
from importlib import util

case_dir = "extension/case"  # 用例所在位置
zip_path = "extension"  # zip压缩前文件所在的位置
zip_dest = "build/extension.zip"  # 生成zip的位置

# 正则表达式模式
name_pattern = (r"(?<=Name[:：]\n)(.*?)"
                r"(?=\s*(Label[:：]|PreCondition[:：]|TestStep[:：]|ExpectedResult[:：]|Parameters[:：]|$))"), re.DOTALL
label_pattern = (r"(?<=Label[:：]\n)(.*?)"
                 r"(?=\s*(PreCondition[:：]|TestStep[:：]|ExpectedResult[:：]|Parameters[:：]|$))"), re.DOTALL
precondition_pattern = (r"(?<=PreCondition[:：]\n)(.*?)"
                        r"(?=\s*(TestStep[:：]|ExpectedResult[:：]|Parameters[:：]|$))"), re.DOTALL
test_step_pattern = (r"(?<=TestStep[:：]\n)(.*?)"
                     r"(?=\s*(TestStep[:：]|ExpectedResult[:：]|Parameters[:：]|$))"), re.DOTALL
expected_result_pattern = (r"(?<=ExpectedResult[:：]\n)(.*?)"
                           r"(?=\s*Author[:：]|Date[:：]|Parameters[:：]|$)"), re.DOTALL
parameters_pattern = (r"(?<=Parameters[:：]\n)(.*?)"
                      r"(?=\s*Author[:：]|Date[:：]|Parameters[:：]|$)"), re.DOTALL


@dataclass
class JsonInfo:
    name: str
    label: str
    precondition: str
    test_step: str
    expected_result: str
    parameters: str
    location: str
    path: str


class CaseDoc:
    """
    生成
    """

    def __init__(self, path):
        """
        初始化参数
        """
        self.root_path = path
        self.case_path_list = []
        self.docstring_dict = {}

        self.walk()

        self.find()

    def walk(self):
        """
        爬文档
        """
        if not os.path.exists(self.root_path):
            raise AttributeError('The path is not found.')

        if not os.path.isdir(self.root_path):
            raise AttributeError('The path should be a folder.')

        for dir_path, _, case_names in os.walk(self.root_path):
            for case_name in case_names:
                if case_name.endswith('.py') and case_name != '__init__.py':
                    self.case_path_list.append(os.path.join(dir_path, case_name))

    def find(self):
        """
        读取case里的注释
        """
        for path in self.case_path_list:
            self.get_case_info(path)

    def get_case_info(self, path):
        with open(path, 'r', encoding='utf-8') as file:
            content = file.read()
            tree = ast.parse(content)

        for node in ast.walk(tree):
            if isinstance(node, ast.ClassDef):
                if node.body and isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, ast.Str):
                    docstring = node.body[0].value.s
                    self.docstring_dict[path] = str(docstring)

    def generate_json(self, dir_path):
        json_path = os.path.join(dir_path, "case.json")
        if os.path.exists(json_path):
            os.remove(json_path)

        self.write_json(json_path)

    def write_json(self, json_path):
        json_list = []
        for path, docstring in self.docstring_dict.items():
            json_info = extract_json_info(docstring, path)
            json_content = {
                "name": json_info.name,
                "label": json_info.label,
                "preset": 0,
                "description": "",
                "preCondition": json_info.precondition,
                "testStep": json_info.test_step,
                "expectedResult": json_info.expected_result,
                "parameters": deal_with_parameters(json_info.parameters),
                "location": json_info.location,
                "path": json_info.path,
                "duration": 0
            }
            json_list.append(json_content)

        # 写入JSON文件
        with open(json_path, 'w', encoding='utf-8') as f:
            # 确保中文能正确写入，使用ensure_ascii=False
            json.dump(json_list, f, ensure_ascii=False, indent=4)


def get_case_name(path):
    return os.path.splitext(os.path.basename(path))[0]


def get_case_location(path):
    return os.path.dirname(path).lstrip(case_dir).lstrip("\\").replace("\\", "/")


def get_case_path(location):
    # 将用例目录添加到sys.path
    dirs = location.strip().split('/')
    dir_path = case_dir
    dir_list = []
    for dir_name in dirs:
        try:
            if dir_path not in sys.path:
                sys.path.append(dir_path)
            dir_path = os.path.join(dir_path, dir_name)
            module_file = os.path.join(dir_path, "__init__.py")
            # 尝试导入用例目录作为模块
            dir_module = None
            if os.path.exists(module_file):
                # 指定模块的文件路径
                spec = util.spec_from_file_location(dir_name, module_file)
                if spec:
                    # 创建模块
                    dir_module = util.module_from_spec(spec)
                    # 加载模块
                    spec.loader.exec_module(dir_module)
            # 读取__init__.py的label字段作为用例目录的中文标签
            if hasattr(dir_module, 'label'):
                dir_list.append(getattr(dir_module, 'label'))
        except Exception as e:
            logging.warning("failed when get the label of package: {}".format(e))

    return '/'.join(dir_list)


def extract_json_info(docstring, case_path):
    name_match = re.search(name_pattern[0], docstring, name_pattern[1])
    label_match = re.search(label_pattern[0], docstring, label_pattern[1])
    precondition_match = re.search(precondition_pattern[0], docstring, precondition_pattern[1])
    test_step_match = re.search(test_step_pattern[0], docstring, test_step_pattern[1])
    expected_result_match = re.search(expected_result_pattern[0], docstring, expected_result_pattern[1])
    parameters_match = re.search(parameters_pattern[0], docstring, parameters_pattern[1])
    # 提取并保存结果
    name = name_match.group(1).strip() \
        if name_match and name_match.group(1).strip() else get_case_name(case_path)
    label = label_match.group(1).strip() if label_match else ""
    precondition = precondition_match.group(1).strip() if precondition_match else ""
    test_step = test_step_match.group(1).strip() if test_step_match else ""
    expected_result = expected_result_match.group(1).strip() if expected_result_match else ""
    parameters = parameters_match.group(1).strip() if parameters_match else ""
    location = get_case_location(case_path)
    path = get_case_path(location)

    return JsonInfo(name, label, precondition, test_step, expected_result, parameters, location, path)


def deal_with_parameters(parameters):
    pattern = re.compile(r'(\d+)\)\s*name:\s*([^;]+);\s*value:\s*["\']?(.*?)["\']?;\s*'
                         r'type:\s*([^;]+);\s*label:\s*([^;]+);\s*description:\s*(.+)')
    # 使用正则表达式找到所有匹配项
    matches = pattern.findall(parameters)
    # 初始化字典数组
    dict_list = []
    # 遍历所有匹配项，构建字典并添加到列表中
    for match in matches:
        # 创建字典
        data_dict = {
            "name": match[1],
            "value": match[2],
            "dataType": match[3],
            "label": match[4],
            "description": match[5]
        }
        # 添加到列表中
        dict_list.append(data_dict)
    return dict_list


if __name__ == '__main__':
    # 1. 生成case.json文档
    doc = CaseDoc(case_dir)
    doc.generate_json(case_dir)
    # 2. 压缩成zip包
    folder_path = os.path.dirname(zip_dest)
    if os.path.exists(folder_path):
        shutil.rmtree(folder_path)
    os.makedirs(folder_path)

    with zipfile.ZipFile(zip_dest, 'w', zipfile.ZIP_DEFLATED) as zipf:
        # 遍历文件夹中的所有文件和子文件夹
        for root, _, files in os.walk(zip_path):
            for file in files:
                # 构建文件的完整路径
                file_path = os.path.join(root, file)
                # 将文件添加到zip文件中，并指定zip内的相对路径
                zipf.write(file_path, os.path.relpath(file_path, zip_path))
